//////////////////////////////////////////////////////////////////////////////////
//                                                                              //
//  This file is part of the buola project (https://code.google.com/p/buola/).  //
//                                                                              //
//  Copyright(c) 2007-2012 Xavi Gratal                                          //
//  gratal AT gmail DOT com                                                     //
//                                                                              //
//  Buola is free software: you can redistribute it and/or modify               //
//  it under the terms of the GNU General Public License as published by        //
//  the Free Software Foundation, either version 3 of the License, or           //
//  (at your option) any later version.                                         //
//                                                                              //
//  Buola is distributed in the hope that it will be useful,                    //
//  but WITHOUT ANY WARRANTY; without even the implied warranty of              //
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the               //
//  GNU General Public License for more details.                                //
//                                                                              //
//  You should have received a copy of the GNU General Public License           //
//  along with buola.  If not, see <http://www.gnu.org/licenses/>.              //
//                                                                              //
//////////////////////////////////////////////////////////////////////////////////

#include <buola/image.h>
#include <buola/mat/transformations.h>

#include <buola/image/font/cfontface.h>
#include <buola/image/font/cscaledfont.h>

#include <buola/image/canyimage.h>

#include <ft2build.h>
#include FT_SYNTHESIS_H
#include FT_OUTLINE_H
#include FT_ADVANCES_H

#define DOUBLE_TO_26_6(d) ((FT_F26Dot6)((d) * 64.0))
#define DOUBLE_FROM_26_6(t) ((double)(t) / 64.0)
#define DOUBLE_TO_16_16(d) ((FT_Fixed)((d) * 65536.0))
#define DOUBLE_FROM_16_16(t) ((double)(t) / 65536.0)

namespace buola { namespace img {

/**
 * cairo_scaled_font_create:
 * @font_face: a #cairo_font_face_t
 * @font_matrix: font space to user space transformation matrix for the
 *       font. In the simplest case of a N point font, this matrix is
 *       just a scale by N, but it can also be used to shear the font
 *       or stretch it unequally along the two axes. See
 *       cairo_set_font_matrix().
 * @ctm: user to device transformation matrix with which the font will
 *       be used.
 * @options: options to use when getting metrics for the font and
 *           rendering with it.
 *
 * Creates a #cairo_scaled_font_t object from a font face and matrices that
 * describe the size of the font and the environment in which it will
 * be used.
 *
 * Return value: a newly created #cairo_scaled_font_t. Destroy with
 *  cairo_scaled_font_destroy()
 **/

CScaledFont::CScaledFont(CFontFace *pFace,const mat::C2DTransform &pFM,const mat::C2DTransform &pTM)
{
    mFontFace=pFace;

    mFTOptions=pFace->GetFTOptions();

    //TODO:something with load_target

    //TODO:something for hashing
    mFontMatrix=pFM;
    mTransformMatrix=pTM;
    //TODO:remove translation components
    mScaleMatrix=mFontMatrix;
    mScaleMatrix*=mTransformMatrix;

    mMaxScale=mTransformMatrix.MaxScale();

    //TODO:create glyph hash table

    //NOTE:here it sets the scale for the unscaled font... i guess it does this every time it needs to access a scaled font
    mFontFace->SetScale(mScaleMatrix);

    FT_Face lFTFace=mFontFace->GetFTFace();
    FT_Size_Metrics *lFTMetrics=&lFTFace->size->metrics;

    /*
     * Get to unscaled metrics so that the upper level can get back to
     * user space
     *
     * Also use this path for bitmap-only fonts.  The other branch uses
     * face members that are only relevant for scalable fonts.  This is
     * detected by simply checking for units_per_EM==0.
     */

    const CFTTransform &lTransform=mFontFace->GetCurrentTransform();

    if (mFTOptions.mHintMetrics!=HINTMETRICS_OFF||lFTFace->units_per_EM == 0)
    {
        double lXFactor,lYFactor;

        if(lTransform.mXScale==0)
            lXFactor = 0;
        else
            lXFactor = 1 / lTransform.mXScale;

        if(lTransform.mYScale==0)
            lYFactor = 0;
        else
            lYFactor = 1 / lTransform.mYScale;

        mFSMetrics.mAscent= DOUBLE_FROM_26_6(lFTMetrics->ascender) * lYFactor;
        mFSMetrics.mDescent= DOUBLE_FROM_26_6(-lFTMetrics->descender) * lYFactor;
        mFSMetrics.mHeight=DOUBLE_FROM_26_6(lFTMetrics->height) * lYFactor;
        if(!IsVertical())
        {
            mFSMetrics.mMaxAdvance=DOUBLE_FROM_26_6(lFTMetrics->max_advance) *lXFactor;
        }
        else
        {
            mFSMetrics.mMaxAdvance=DOUBLE_FROM_26_6(lFTMetrics->max_advance) *lYFactor;
        }
    }
    else
    {
        double lScale=lFTFace->units_per_EM;

        mFSMetrics.mAscent=lFTFace->ascender / lScale;
        mFSMetrics.mDescent=-lFTFace->descender / lScale;
        mFSMetrics.mHeight=lFTFace->height / lScale;
        if(!IsVertical())
        {
            mFSMetrics.mMaxAdvance=lFTFace->max_advance_width/lScale;
        }
        else
        {
            mFSMetrics.mMaxAdvance=lFTFace->max_advance_height/lScale;
        }
    }

    mMetrics.mAscent=mFSMetrics.mAscent*lTransform.mYScale;
    mMetrics.mDescent=mFSMetrics.mDescent*lTransform.mYScale;
    mMetrics.mHeight=mFSMetrics.mHeight*lTransform.mYScale;
    mMetrics.mMaxAdvance=mFSMetrics.mMaxAdvance*(IsVertical()?lTransform.mYScale:lTransform.mYScale);

    CGlyphInfo lGlyph;

    lGlyph.mGlyph=mFontFace->GetGlyph(L'x');
    FillGlyphInfo(lGlyph);
    mMetrics.mXHeight=lGlyph.mH;
    lGlyph.mGlyph=mFontFace->GetGlyph(L' ');
    FillGlyphInfo(lGlyph);
    mMetrics.mSpaceWidth=lGlyph.mXOff;
}



bool CScaledFont::IsVertical()
{
    return (mFTOptions.mLoadFlags&FT_LOAD_VERTICAL_LAYOUT);
}

void CScaledFont::FillGlyphInfo(CGlyphInfo &pG)
{
/**
 * cairo_scaled_font_glyph_extents:
 * @scaled_font: a #cairo_scaled_font_t
 * @glyphs: an array of glyph IDs with X and Y offsets.
 * @num_glyphs: the number of glyphs in the @glyphs array
 * @extents: a #cairo_text_extents_t which to store the retrieved extents.
 *
 * Gets the extents for an array of glyphs. The extents describe a
 * user-space rectangle that encloses the "inked" portion of the
 * glyphs, (as they would be drawn by cairo_show_glyphs() if the cairo
 * graphics state were set to the same font_face, font_matrix, ctm,
 * and font_options as @scaled_font).  Additionally, the x_advance and
 * y_advance values indicate the amount by which the current point
 * would be advanced by cairo_show_glyphs().
 *
 * Note that whitespace glyphs do not contribute to the size of the
 * rectangle (extents.width and extents.height).
 **/
    CRect_d lRect(0,0,0,0);

    CScaledGlyph *lScaledGlyph=LookupGlyph(pG.mGlyph,GLYPHDATA_METRICS);

    if(lScaledGlyph->mMetrics.mSize.x!=0&&lScaledGlyph->mMetrics.mSize.y!=0)
    {
        lRect={lScaledGlyph->mMetrics.mBearing,lScaledGlyph->mMetrics.mSize};
    }

    pG.mX=lRect.l;
    pG.mY=lRect.t;
    pG.mW=lRect.w();
    pG.mH=lRect.h();

    pG.mXOff=lScaledGlyph->mMetrics.mAdvance.x+0.5; //round up
    pG.mYOff=lScaledGlyph->mMetrics.mAdvance.y+0.5; //round up
}

/**
 * _cairo_scaled_glyph_lookup:
 * @scaled_font: a #cairo_scaled_font_t
 * @index: the glyph to create
 * @info: a #cairo_scaled_glyph_info_t marking which portions of
 * the glyph should be filled in.
 * @scaled_glyph_ret: a #cairo_scaled_glyph_t where the glyph
 * is returned.
 *
 * If the desired info is not available, (for example, when trying to
 * get INFO_PATH with a bitmapped font), this function will return
 * %CAIRO_INT_STATUS_UNSUPPORTED.
 *
 * Note: This function must be called with the scaled font frozen, and it must
 * remain frozen for as long as the @scaled_glyph_ret is alive. (If the scaled
 * font was not frozen, then there is no guarantee that the glyph would not be
 * evicted before you tried to access it.) See
 * _cairo_scaled_font_freeze_cache() and _cairo_scaled_font_thaw_cache().
 *
 * Returns: a glyph with the requested portions filled in. Glyph
 * lookup is cached and glyph will be automatically freed along
 * with the scaled_font so no explicit free is required.
 * @info can be one or more of:
 *  %CAIRO_SCALED_GLYPH_INFO_METRICS - glyph metrics and bounding box
 *  %CAIRO_SCALED_GLYPH_INFO_SURFACE - surface holding glyph image
 *  %CAIRO_SCALED_GLYPH_INFO_PATH - path holding glyph outline in device space
 **/

CScaledGlyph *CScaledFont::LookupGlyph(TGlyphIndex pIndex,EGlyphData pData)
{
    CScaledGlyph *lRet=nullptr;

    tGlyphCache::iterator lFind=mGlyphCache.find(pIndex);

    if(lFind==mGlyphCache.end())
    {
        //TODO:this should actually be a cache, with finite storage, and use pages, and so on, but for now, just create it
        lRet=new CScaledGlyph;
        lRet->mIndex=pIndex;
        lRet->mCacheEntry=nullptr;
        lRet->mValid=GLYPHDATA_NONE;

        InitScaledGlyph(lRet,pData|GLYPHDATA_METRICS);

        mGlyphCache[pIndex]=lRet;
    }
    else
    {
        lRet=lFind->second;
        EGlyphData lMissingData=pData&~lRet->mValid;
        if(lMissingData)
        {
            InitScaledGlyph(lRet,lMissingData);
        }
    }

    EGlyphData lStillMissing=pData&~lRet->mValid;

    if(lStillMissing)
        throw XInternal("can't retrieve all the data\n");

    return lRet;
}

void CScaledFont::InitScaledGlyph(CScaledGlyph *pG,EGlyphData pData)
{
    CTextExtents lFSMetrics;
    int lLoadFlags=mFTOptions.mLoadFlags;

    FT_Face lFace=mFontFace->GetFTFace();
    bool lVerticalLayout=false;

    ///\todo why?? what is that?
    /* Ignore global advance unconditionally */
    lLoadFlags|=FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH;

    //we don't want bitmaps, they are not antialiased (as far as I've seen)
    lLoadFlags|=FT_LOAD_NO_BITMAP;
    
    ///\todo check what to do about hinting... it seems we are hinting too much.

    /*
     * Don't pass FT_LOAD_VERTICAL_LAYOUT to FT_Load_Glyph here as
     * suggested by freetype people.
     */
    if(lLoadFlags&FT_LOAD_VERTICAL_LAYOUT)
    {
        lLoadFlags&=~FT_LOAD_VERTICAL_LAYOUT;
        lVerticalLayout=false;
    }

    FT_Error lError=FT_Load_Glyph(lFace,pG->mIndex,lLoadFlags);

    /* XXX ignoring all other errors for now.  They are not fatal, typically
     * just a glyph-not-found. */
    if(lError==FT_Err_Out_Of_Memory)
    {
        throw XMemory();
    }

    FT_GlyphSlot lGlyph=lFace->glyph;

    /*
     * embolden glyphs if requested
     */
    if(mFTOptions.mExtraFlags&FTEXTRA_EMBOLDEN)
        FT_GlyphSlot_Embolden (lGlyph);

    if(lVerticalLayout)
    {
        FT_Vector lVector;

        lVector.x = lGlyph->metrics.vertBearingX - lGlyph->metrics.horiBearingX;
        lVector.y = -lGlyph->metrics.vertBearingY - lGlyph->metrics.horiBearingY;

        if (lGlyph->format == FT_GLYPH_FORMAT_OUTLINE)
        {
            FT_Vector_Transform(&lVector,mFontFace->GetCurrentFTShape());
            FT_Outline_Translate(&lGlyph->outline, lVector.x, lVector.y);
        }
        else if (lGlyph->format == FT_GLYPH_FORMAT_BITMAP)
        {
            lGlyph->bitmap_left += lVector.x / 64;
            lGlyph->bitmap_top  += lVector.y / 64;
        }
    }

    if(pData&GLYPHDATA_METRICS)
    {
        bool lHintMetrics=(mFTOptions.mHintMetrics!=HINTMETRICS_OFF);

        const CFTTransform &lTransform=mFontFace->GetCurrentTransform();

        /*
        * Compute font-space metrics
        */
        FT_Glyph_Metrics *lMetrics=&lGlyph->metrics;

        double lXFactor,lYFactor;

        if(lTransform.mXScale==0)
            lXFactor = 0;
        else
            lXFactor = 1 / lTransform.mXScale;

        if(lTransform.mYScale==0)
            lYFactor = 0;
        else
            lYFactor = 1 / lTransform.mYScale;

        /*
        * Note: Y coordinates of the horizontal bearing need to be negated.
        *
        * Scale metrics back to glyph space from the scaled glyph space returned
        * by FreeType
        *
        * If we want hinted metrics but aren't asking for hinted glyphs from
        * FreeType, then we need to do the metric hinting ourselves.
        */

        if(lHintMetrics&&(lLoadFlags&FT_LOAD_NO_HINTING))
        {
            FT_Pos x1, x2;
            FT_Pos y1, y2;
            FT_Pos lAdvance;

            if (!lVerticalLayout)
            {
                x1 = (lMetrics->horiBearingX) & -64;
                x2 = (lMetrics->horiBearingX + lMetrics->width + 63) & -64;
                y1 = (-lMetrics->horiBearingY) & -64;
                y2 = (-lMetrics->horiBearingY + lMetrics->height + 63) & -64;

                lAdvance = ((lMetrics->horiAdvance + 32) & -64);

                lFSMetrics.mBearing.x=DOUBLE_FROM_26_6 (x1) * lXFactor;
                lFSMetrics.mBearing.y=DOUBLE_FROM_26_6 (y1) * lYFactor;

                lFSMetrics.mSize.x  = DOUBLE_FROM_26_6 (x2 - x1) * lXFactor;
                lFSMetrics.mSize.y  = DOUBLE_FROM_26_6 (y2 - y1) * lYFactor;

                lFSMetrics.mAdvance.x=DOUBLE_FROM_26_6 (lAdvance) * lXFactor;
                lFSMetrics.mAdvance.y=0;
            }
            else
            {
                x1 = (lMetrics->vertBearingX) & -64;
                x2 = (lMetrics->vertBearingX + lMetrics->width + 63) & -64;
                y1 = (-lMetrics->vertBearingY) & -64;
                y2 = (-lMetrics->vertBearingY + lMetrics->height + 63) & -64;

                lAdvance = ((lMetrics->vertAdvance + 32) & -64);

                lFSMetrics.mBearing.x=DOUBLE_FROM_26_6 (x1) * lXFactor;
                lFSMetrics.mBearing.y=DOUBLE_FROM_26_6 (y1) * lYFactor;

                lFSMetrics.mSize.x  = DOUBLE_FROM_26_6 (x2 - x1) * lXFactor;
                lFSMetrics.mSize.y  = DOUBLE_FROM_26_6 (y2 - y1) * lYFactor;

                lFSMetrics.mAdvance.x = 0;
                lFSMetrics.mAdvance.y = DOUBLE_FROM_26_6 (lAdvance) * lXFactor;
            }
        }
        else
        {
            lFSMetrics.mSize.x = DOUBLE_FROM_26_6 (lMetrics->width) * lXFactor;
            lFSMetrics.mSize.y = DOUBLE_FROM_26_6 (lMetrics->height) * lYFactor;

            if (!lVerticalLayout)
            {
                lFSMetrics.mBearing.x=DOUBLE_FROM_26_6 (lMetrics->horiBearingX) * lXFactor;
                lFSMetrics.mBearing.y=DOUBLE_FROM_26_6 (lMetrics->horiBearingY) * lYFactor;

                if(lHintMetrics||lGlyph->format != FT_GLYPH_FORMAT_OUTLINE)
                    lFSMetrics.mAdvance.x = DOUBLE_FROM_26_6 (lMetrics->horiAdvance) * lXFactor;
                else
                    lFSMetrics.mAdvance.x = DOUBLE_FROM_16_16 (lGlyph->linearHoriAdvance) * lXFactor;
                lFSMetrics.mAdvance.y = 0;
            }
            else
            {
                lFSMetrics.mBearing.x=DOUBLE_FROM_26_6 (lMetrics->vertBearingX) * lXFactor;
                lFSMetrics.mBearing.y=DOUBLE_FROM_26_6 (lMetrics->vertBearingY) * lYFactor;

                if(lHintMetrics||lGlyph->format != FT_GLYPH_FORMAT_OUTLINE)
                    lFSMetrics.mAdvance.y = DOUBLE_FROM_26_6 (lMetrics->vertAdvance) * lXFactor;
                else
                    lFSMetrics.mAdvance.y = DOUBLE_FROM_16_16 (lGlyph->linearVertAdvance) * lXFactor;
                lFSMetrics.mAdvance.x = 0;
            }
        }

        bool lFirst=true;
        double lHM,lWM;
        double lMinUserX=0,lMaxUserX=0,lMinUserY=0,lMaxUserY=0;
        double lMinDeviceX=0,lMaxDeviceX=0,lMinDeviceY=0,lMaxDeviceY=0;

        pG->mFSMetrics=lFSMetrics;

        for(lHM=0.0;lHM<=1.0;lHM+=1.0)
        {
            for(lWM=0.0;lWM<=1.0;lWM+=1.0)
            {
                /* Transform this corner to user space */
                CPoint_d lPoint(lFSMetrics.mBearing.x+lFSMetrics.mSize.x*lWM,lFSMetrics.mBearing.y+lFSMetrics.mSize.y*lHM);
                CPoint_d lUserPoint=mFontMatrix*lPoint;

                if(lFirst)
                {
                    lMinUserX=lMaxUserX=lUserPoint.x;
                    lMinUserY=lMaxUserY=lUserPoint.y;
                }
                else
                {
                    lMinUserX=std::min(lMinUserX,lUserPoint.x);
                    lMaxUserX=std::max(lMaxUserX,lUserPoint.x);
                    lMinUserY=std::min(lMinUserY,lUserPoint.y);
                    lMaxUserY=std::max(lMaxUserY,lUserPoint.y);
                }

                /* Transform this corner to device space from glyph origin */
                CPoint_d lDevicePoint=transform_vector(mScaleMatrix,lPoint);

                if(lFirst)
                {
                    lMinDeviceX=lMaxDeviceX=lDevicePoint.x;
                    lMinDeviceY=lMaxDeviceY=lDevicePoint.y;
                }
                else
                {
                    lMinDeviceX=std::min(lMinDeviceX,lDevicePoint.x);
                    lMaxDeviceX=std::max(lMaxDeviceX,lDevicePoint.x);
                    lMinDeviceY=std::min(lMinDeviceY,lDevicePoint.y);
                    lMaxDeviceY=std::max(lMaxDeviceY,lDevicePoint.y);
                }

                lFirst=false;
            }
        }

        pG->mMetrics.mBearing.x=lMinUserX;
        pG->mMetrics.mBearing.y=lMinUserY;
        pG->mMetrics.mSize.x=lMaxUserX-lMinUserX;
        pG->mMetrics.mSize.y=lMaxUserY-lMinUserY;

        pG->mMetrics.mAdvance=transform_vector(mFontMatrix,lFSMetrics.mAdvance);

        pG->mBBox={lMinDeviceX,lMinDeviceY,lMaxDeviceX,lMaxDeviceY};
        CPoint_d lDeviceAdvance=transform_vector(mScaleMatrix,lFSMetrics.mAdvance);

        pG->mAdvance={(int)round(lDeviceAdvance.x),(int)round(lDeviceAdvance.y)};
        
        pG->mValid|=GLYPHDATA_METRICS;
    }

    if(pData&GLYPHDATA_PATH)
    {
        throw XInvalid("trying to get path for glyph");
        //TODO:there was something about the surface calculation trashing the outline, so I changed the order and
        //did this first... check if it is correct
        //TODO:cairo_path_fixed_t *path = nullptr; /* hide compiler warning */

        if(lGlyph->format==FT_GLYPH_FORMAT_OUTLINE)
        {
            //TODO
            //_decompose_glyph_outline (face, &scaled_font->ft_options.base,&path);
        }
        else
        {
            throw XInvalid("trying to get outline from unsupported glyph");
        }

        //TODO:pG->mPath=somepaththaticreateabove
        //_cairo_scaled_glyph_set_path (scaled_glyph,
        //              &scaled_font->base,
        //              path);
    }

    if(pData&GLYPHDATA_IMAGE)
    {
///\todo decide when to use outline version
#if 0
        if(lGlyph->format == FT_GLYPH_FORMAT_OUTLINE)
        {
            FT_Outline *lOutline=&lGlyph->outline;
            FT_Bitmap lBitmap;
            FT_BBox lCBox;
            FT_Matrix lMatrix;

            int lHMul = 1;
            int lVMul = 1;
            //bool lSubpixel=false;

            FT_Outline_Get_CBox (lOutline, &lCBox);

            CRect_i lRect(lCBox.xMin>>6,lCBox.yMin>>6,(lCBox.xMax+63)>>6,(lCBox.yMax+63)>>6);
            CSize_i lSize=lRect.Size();

            uint32_t lStride=(lSize.x+3)&~3;

            if(lSize.x*lSize.y!=0)
            {
                lMatrix.xx = lMatrix.yy = 0x10000L;
                lMatrix.xy = lMatrix.yx = 0;

                switch (mFTOptions.mAntialias)
                {
                case ANTIALIAS_NONE:
                    lBitmap.pixel_mode = FT_PIXEL_MODE_MONO;
                    lBitmap.num_grays  = 1;
                    lStride = ((lSize.x + 31) & ~31) >> 3;
                    break;
                case ANTIALIAS_DEFAULT:
                case ANTIALIAS_GRAY:
                    lBitmap.pixel_mode = FT_PIXEL_MODE_GRAY;
                    lBitmap.num_grays  = 256;
                    lStride = (lSize.x + 3) & ~3;
                    break;
                case ANTIALIAS_SUBPIXEL:
                    switch (mFTOptions.mSubpixel)
                    {
                    case SUBPIXEL_RGB:
                    case SUBPIXEL_BGR:
                    case SUBPIXEL_DEFAULT:
                    default:
                        lMatrix.xx *= 3;
                        lHMul = 3;
                        //lSubpixel = true;
                        break;
                    case SUBPIXEL_VRGB:
                    case SUBPIXEL_VBGR:
                        lMatrix.yy *= 3;
                        lVMul = 3;
                        //lSubpixel = true;
                        break;
                    }
                    FT_Outline_Transform (lOutline, &lMatrix);

                    lBitmap.pixel_mode = FT_PIXEL_MODE_GRAY;
                    lBitmap.num_grays  = 256;
                    lStride = (lSize.x * lHMul + 3) & ~3;
                }

                lBitmap.pitch = lStride;
                lBitmap.width = lSize.x*lHMul;
                lBitmap.rows = lSize.y*lVMul;
                lBitmap.buffer = alloc_array_z<uint8_t> (lStride*lBitmap.rows);

                FT_Outline_Translate (lOutline, -lCBox.xMin*lHMul, -lCBox.yMin*lVMul);

                if (FT_Outline_Get_Bitmap (lGlyph->library, lOutline, &lBitmap) != 0)
                {
                    free (lBitmap.buffer);
                    throw XInternal("couldn't create bitmap for glyph");
                }

                pG->mImage=GetImageFromFTBitmap(&lBitmap,true);

                /*
                * Note: the font's coordinate system is upside down from ours, so the
                * Y coordinate of the control box needs to be negated.  Moreover, device
                * offsets are position of glyph origin relative to top left while xMin
                * and yMax are offsets of top left relative to origin.  Another negation.
                */
                //TODO:something about the coordinate system being upside down
//                cairo_surface_set_device_offset (&(*surface)->base,
//                                floor (-(double) cbox.xMin / 64.0),
//                                floor (+(double) cbox.yMax / 64.0));
            }
            else
            {
                pG->mImage=nullptr;
            }
        }
        else
#endif
        {
            /* According to the FreeType docs, glyphslot->format could be
            * something other than FT_GLYPH_FORMAT_OUTLINE or
            * FT_GLYPH_FORMAT_BITMAP. Calling FT_Render_Glyph gives FreeType
            * the opportunity to convert such to
            * bitmap. FT_GLYPH_FORMAT_COMPOSITE will not be encountered since
            * we avoid the FT_LOAD_NO_RECURSE flag.
            */
            lError = FT_Render_Glyph (lGlyph, FT_RENDER_MODE_NORMAL);
            /* XXX ignoring all other errors for now.  They are not fatal, typically
            * just a glyph-not-found. */
            if (lError == FT_Err_Out_Of_Memory)
                throw XMemory("no memory in FT_Render_Glyph");

            pG->mImage=GetImageFromFTBitmap(&lGlyph->bitmap,false);

            /*
            * Note: the font's coordinate system is upside down from ours, so the
            * Y coordinate of the control box needs to be negated.  Moreover, device
            * offsets are position of glyph origin relative to top left while
            * bitmap_left and bitmap_top are offsets of top left relative to origin.
            * Another negation.
            */
            //TODO:something about the coordinate system being upside down
//            cairo_surface_set_device_offset (&(*surface)->base,
//                            -glyphslot->bitmap_left,
//                            +glyphslot->bitmap_top);
        }

        pG->mValid|=GLYPHDATA_IMAGE;
    }
}

/* Empirically-derived subpixel filtering values thanks to Keith
 * Packard and libXft. */
static const int sFilters[3][3]=
{
    /* red */
#if 0
    {    65538*4/7,65538*2/7,65538*1/7 },
    /* green */
    {    65536*1/4, 65536*2/4, 65537*1/4 },
    /* blue */
    {    65538*1/7,65538*2/7,65538*4/7 },
#endif
    {    65538*9/13,65538*3/13,65538*1/13 },
    /* green */
    {    65538*1/6, 65538*4/6, 65538*1/6 },
    /* blue */
    {    65538*1/13,65538*3/13,65538*9/13 }
};

img::CAnyImage *CScaledFont::GetImageFromFTBitmap(FT_Bitmap *pBitmap,bool pOwnBuffer)
{
    CSize_i lSize(pBitmap->width,pBitmap->rows);

    if(lSize.x*lSize.y==0)
        return nullptr;

    if(pBitmap->pixel_mode==FT_PIXEL_MODE_MONO)
    {
        ///\todo: this is not supported yet, need 1-bit images...

//                 lStride = (((lSize.x + 31) & ~31) >> 3);
//
//                 if(pOwnBuffer)
//                 {
//                     lData=pBitmap->buffer;
//                 }
//                 else
//                 {
//                     lData=alloc_array<uint8_t>(lSize.y*lStride);
//
//                     if (lStride == pBitmap->pitch)
//                     {
//                         memcpy (lData, pBitmap->buffer, lStride * lSize.y);
//                     }
//                     else
//                     {
//                         unsigned char *lSrc, *lDst;
//
//                         lSrc=pBitmap->buffer;
//                         lDst=lData;
//
//                         for (int i = lSize.y;i;i++)
//                         {
//                             memcpy (lDst,lSrc,pBitmap->pitch);
//                             memset (lDst+pBitmap->pitch, (uint8_t)0, lStride - pBitmap->pitch);
//
//                             lSrc += pBitmap->pitch;
//                             lDst += lStride;
//                         }
//                     }
//                 }
//
//         //now we should swap the bits!
//         #if 0
//
//                 {
//                     uint8_t   *lD = lData;
//                     int     lCount = lStride * lHeight;
//
//                     while (lCount--)
//                     {
//                         *lD = CAIRO_BITSWAP8 (*d);
//                 d++;
//                 }
//             }
//         #endif
//
//                 ///\todo:fix this
        msg_warn() << "format should be GRAY 1!!\n";
        return nullptr;
    }
    else if(pBitmap->pixel_mode==FT_PIXEL_MODE_LCD||pBitmap->pixel_mode==FT_PIXEL_MODE_LCD_V||
            pBitmap->pixel_mode==FT_PIXEL_MODE_GRAY)
    {
        if(mFTOptions.mAntialias==ANTIALIAS_SUBPIXEL)
        {
            int         x, y;
            uint8_t   *lInLine,*lOutLine,*lIn;
            uint32_t    *lOut;
            uint32_t r,g,b;
            int         rf, gf, bf;
            int         s;
            int         o, os;
            uint8_t *lDataRGBA;
            uint32_t lWidthRGBA,lStrideRGBA;
            int         lVMul = 1;
            int         lHMul = 1;

            switch (mFTOptions.mSubpixel)
            {
            case SUBPIXEL_DEFAULT:
            case SUBPIXEL_BGR:
            case SUBPIXEL_RGB:
            default:
                lSize.x /= 3;
                lHMul = 3;
                break;
            case SUBPIXEL_VBGR:
            case SUBPIXEL_VRGB:
                lVMul = 3;
                lSize.y /= 3;
                break;
            }
            /*
            * Filter the glyph to soften the color fringes
            */
            lWidthRGBA=lSize.x;
            lStrideRGBA = (lWidthRGBA * 4 + 3) & ~3;
            lDataRGBA = alloc_array_z<uint8_t>(lStrideRGBA*lSize.y);

            os = 1;
            switch (mFTOptions.mSubpixel)
            {
            case SUBPIXEL_VRGB:
                os = pBitmap->pitch;
            case SUBPIXEL_DEFAULT:
            case SUBPIXEL_RGB:
            default:
                rf = 0;
                gf = 1;
                bf = 2;
                break;
            case SUBPIXEL_VBGR:
                os = pBitmap->pitch;
            case SUBPIXEL_BGR:
                bf = 0;
                gf = 1;
                rf = 2;
                break;
            }

            lInLine = pBitmap->buffer;
            lOutLine = lDataRGBA;

            for (y = 0; y < lSize.y; y++)
            {
                lIn = lInLine;
                lOut = (uint32_t*)lOutLine;
                lInLine += pBitmap->pitch * lVMul;
                lOutLine += lStrideRGBA;
                for (x = 0; x < lSize.x * lHMul; x += lHMul)
                {
                    r = g = b = 0;
                    o = 0;
                    for (s = 0; s < 3; s++)
                    {
                        r += sFilters[rf][s]*lIn[x+o];
                        g += sFilters[gf][s]*lIn[x+o];
                        b += sFilters[bf][s]*lIn[x+o];
                        o += os;
                    }
                    r/=65536;
                    g/=65536;
                    b/=65536;
                    *lOut++ = (g << 24) | (r << 16) | (g << 8) | b;
                }
            }

            /* Images here are stored in native format. The
            * backend must convert to its own format as needed
            */

            if (pOwnBuffer)
                free (pBitmap->buffer);

            return new img::CAnyImage(img::CImage_rgba8(nSteal,lSize,lDataRGBA,lStrideRGBA));
        }
        else
        {
            uint8_t *lData;

            if (pOwnBuffer)
                lData=pBitmap->buffer;
            else
                lData=dup_array<uint8_t>(pBitmap->buffer,lSize.y*pBitmap->pitch);

            img::CImage_gray8 lImage(nSteal,lSize,lData,pBitmap->pitch);

            return new img::CAnyImage(std::move(lImage));
        }
    }
    else
    {
        if (pOwnBuffer)
            free (pBitmap->buffer);

        throw XInvalid("wrong type of FTPixel");
    }
}

CScaledFont *CScaledFont::Create(CFontFace *pFace,const mat::C2DTransform &pFM,const mat::C2DTransform &pTM)
{
    /* Note that degenerate ctm or font_matrix *are* allowed.
    * We want to support a font size of 0. */

    //Cairo does some caching here... it first check if it matches the mru (most recently used) scaled font,
           //then checks in a hash table. we are doing caching at a higher level, so I'm not sure it is necessary to do
           //it here too.

    /* Otherwise create it and insert it into the hash table. */
    return new CScaledFont(pFace,pFM,pTM);
}

/*namespace img*/ } /*namespace buola*/ }
































#if 0






/* Global Glyph Cache
 *
 * We maintain a global pool of glyphs split between all active fonts. This
 * allows a heavily used individual font to cache more glyphs than we could
 * manage if we used per-font glyph caches, but at the same time maintains
 * fairness across all fonts and provides a cap on the maximum number of
 * global glyphs.
 *
 * The glyphs are allocated in pages, which are capped in the global pool.
 * Using pages means we can reduce the frequency at which we have to probe the
 * global pool and ameliorates the memory allocation pressure.
 */


/*
 *  Notes:
 *
 *  To store rasterizations of glyphs, we use an image surface and the
 *  device offset to represent the glyph origin.
 *
 *  A device_transform converts from device space (a conceptual space) to
 *  surface space.  For simple cases of translation only, it's called a
 *  device_offset and is public API (cairo_surface_[gs]et_device_offset()).
 *  A possibly better name for those functions could have been
 *  cairo_surface_[gs]et_origin().  So, that's what they do: they set where
 *  the device-space origin (0,0) is in the surface.  If the origin is inside
 *  the surface, device_offset values are positive.  It may look like this:
 *
 *  Device space:
 *        (-x,-y) <-- negative numbers
 *           +----------------+
 *           |      .         |
 *           |      .         |
 *           |......(0,0) <---|-- device-space origin
 *           |                |
 *           |                |
 *           +----------------+
 *                    (width-x,height-y)
 *
 *  Surface space:
 *         (0,0) <-- surface-space origin
 *           +---------------+
 *           |      .        |
 *           |      .        |
 *           |......(x,y) <--|-- device_offset
 *           |               |
 *           |               |
 *           +---------------+
 *                     (width,height)
 *
 *  In other words: device_offset is the coordinates of the device-space
 *  origin relative to the top-left of the surface.
 *
 *  We use device offsets in a couple of places:
 *
 *    - Public API: To let toolkits like Gtk+ give user a surface that
 *      only represents part of the final destination (say, the expose
 *      area), but has the same device space as the destination.  In these
 *      cases device_offset is typically negative.  Example:
 *
 *           application window
 *           +---------------+
 *           |      .        |
 *           | (x,y).        |
 *           |......+---+    |
 *           |      |   | <--|-- expose area
 *           |      +---+    |
 *           +---------------+
 *
 *      In this case, the user of cairo API can set the device_space on
 *      the expose area to (-x,-y) to move the device space origin to that
 *      of the application window, such that drawing in the expose area
 *      surface and painting it in the application window has the same
 *      effect as drawing in the application window directly.  Gtk+ has
 *      been using this feature.
 *
 *    - Glyph surfaces: In most font rendering systems, glyph surfaces
 *      have an origin at (0,0) and a bounding box that is typically
 *      represented as (x_bearing,y_bearing,width,height).  Depending on
 *      which way y progresses in the system, y_bearing may typically be
 *      negative (for systems similar to cairo, with origin at top left),
 *      or be positive (in systems like PDF with origin at bottom left).
 *      No matter which is the case, it is important to note that
 *      (x_bearing,y_bearing) is the coordinates of top-left of the glyph
 *      relative to the glyph origin.  That is, for example:
 *
 *      Scaled-glyph space:
 *
 *        (x_bearing,y_bearing) <-- negative numbers
 *           +----------------+
 *           |      .         |
 *           |      .         |
 *           |......(0,0) <---|-- glyph origin
 *           |                |
 *           |                |
 *           +----------------+
 *                    (width+x_bearing,height+y_bearing)
 *
 *      Note the similarity of the origin to the device space.  That is
 *      exactly how we use the device_offset to represent scaled glyphs:
 *      to use the device-space origin as the glyph origin.
 *
 *  Now compare the scaled-glyph space to device-space and surface-space
 *  and convince yourself that:
 *
 *  (x_bearing,y_bearing) = (-x,-y) = - device_offset
 *
 *  That's right.  If you are not convinced yet, contrast the definition
 *  of the two:
 *
 *  "(x_bearing,y_bearing) is the coordinates of top-left of the
 *   glyph relative to the glyph origin."
 *
 *  "In other words: device_offset is the coordinates of the
 *   device-space origin relative to the top-left of the surface."
 *
 *  and note that glyph origin = device-space origin.
 */

/* Public font API follows. */

static inline cairo_bool_t
_range_contains_glyph (const cairo_box_t *extents,
               cairo_fixed_t left,
               cairo_fixed_t top,
               cairo_fixed_t right,
               cairo_fixed_t bottom)
{
    return right > extents->p1.x &&
       left < extents->p2.x &&
       bottom > extents->p1.y &&
       top < extents->p2.y;
}

/*
 * Compute a device-space bounding box for the glyphs.
 */
cairo_status_t
_cairo_scaled_font_glyph_device_extents (cairo_scaled_font_t     *scaled_font,
                     const cairo_glyph_t     *glyphs,
                     int                      num_glyphs,
                     cairo_rectangle_int_t   *extents,
                     cairo_bool_t *overlap_out)
{
    cairo_status_t status = CAIRO_STATUS_SUCCESS;
    cairo_box_t box = { { INT_MAX, INT_MAX }, { INT_MIN, INT_MIN }};
    cairo_scaled_glyph_t *glyph_cache[64];
    cairo_bool_t overlap = overlap_out ? FALSE : TRUE;
    int i;

    if (unlikely (scaled_font->status))
    return scaled_font->status;

    _cairo_scaled_font_freeze_cache (scaled_font);

    memset (glyph_cache, 0, sizeof (glyph_cache));

    for (i = 0; i < num_glyphs; i++) {
    cairo_scaled_glyph_t    *scaled_glyph;
    cairo_fixed_t x, y, x1, y1, x2, y2;
    int cache_index = glyphs[i].index % ARRAY_LENGTH (glyph_cache);

    scaled_glyph = glyph_cache[cache_index];
    if (scaled_glyph == nullptr ||
        _cairo_scaled_glyph_index (scaled_glyph) != glyphs[i].index)
    {
        status = _cairo_scaled_glyph_lookup (scaled_font,
                         glyphs[i].index,
                         CAIRO_SCALED_GLYPH_INFO_METRICS,
                         &scaled_glyph);
        if (unlikely (status))
        break;

        glyph_cache[cache_index] = scaled_glyph;
    }

    x = _cairo_fixed_from_double (glyphs[i].x);
    x1 = x + scaled_glyph->bbox.p1.x;
    x2 = x + scaled_glyph->bbox.p2.x;

    y = _cairo_fixed_from_double (glyphs[i].y);
    y1 = y + scaled_glyph->bbox.p1.y;
    y2 = y + scaled_glyph->bbox.p2.y;

    if (overlap == FALSE)
        overlap = _range_contains_glyph (&box, x1, y1, x2, y2);

    if (x1 < box.p1.x) box.p1.x = x1;
    if (x2 > box.p2.x) box.p2.x = x2;
    if (y1 < box.p1.y) box.p1.y = y1;
    if (y2 > box.p2.y) box.p2.y = y2;
    }

    _cairo_scaled_font_thaw_cache (scaled_font);
    if (unlikely (status))
    return _cairo_scaled_font_set_error (scaled_font, status);

    if (box.p1.x < box.p2.x) {
    _cairo_box_round_to_rectangle (&box, extents);
    } else {
    extents->x = extents->y = 0;
    extents->width = extents->height = 0;
    }

    if (overlap_out != nullptr)
    *overlap_out = overlap;

    return CAIRO_STATUS_SUCCESS;
}
/* Add a single-device-unit rectangle to a path. */
static cairo_status_t
_add_unit_rectangle_to_path (cairo_path_fixed_t *path,
                 cairo_fixed_t x,
                 cairo_fixed_t y)
{
    cairo_status_t status;

    status = _cairo_path_fixed_move_to (path, x, y);
    if (unlikely (status))
    return status;

    status = _cairo_path_fixed_rel_line_to (path,
                        _cairo_fixed_from_int (1),
                        _cairo_fixed_from_int (0));
    if (unlikely (status))
    return status;

    status = _cairo_path_fixed_rel_line_to (path,
                        _cairo_fixed_from_int (0),
                        _cairo_fixed_from_int (1));
    if (unlikely (status))
    return status;

    status = _cairo_path_fixed_rel_line_to (path,
                        _cairo_fixed_from_int (-1),
                        _cairo_fixed_from_int (0));
    if (unlikely (status))
    return status;

    return _cairo_path_fixed_close_path (path);
}

/**
 * _trace_mask_to_path:
 * @bitmap: An alpha mask (either %CAIRO_FORMAT_A1 or %CAIRO_FORMAT_A8)
 * @path: An initialized path to hold the result
 *
 * Given a mask surface, (an alpha image), fill out the provided path
 * so that when filled it would result in something that approximates
 * the mask.
 *
 * Note: The current tracing code here is extremely primitive. It
 * operates only on an A1 surface, (converting an A8 surface to A1 if
 * necessary), and performs the tracing by drawing a little square
 * around each pixel that is on in the mask. We do not pretend that
 * this is a high-quality result. But we are leaving it up to someone
 * who cares enough about getting a better result to implement
 * something more sophisticated.
 **/
static cairo_status_t
_trace_mask_to_path (cairo_image_surface_t *mask,
             cairo_path_fixed_t *path,
             double tx, double ty)
{
    const uint8_t *row;
    int rows, cols, bytes_per_row;
    int x, y, bit;
    double xoff, yoff;
    cairo_fixed_t x0, y0;
    cairo_fixed_t px, py;
    cairo_status_t status;

    mask = _cairo_image_surface_coerce_to_format (mask, CAIRO_FORMAT_A1);
    status = mask->base.status;
    if (unlikely (status))
    return status;

    cairo_surface_get_device_offset (&mask->base, &xoff, &yoff);
    x0 = _cairo_fixed_from_double (tx - xoff);
    y0 = _cairo_fixed_from_double (ty - yoff);

    bytes_per_row = (mask->width + 7) / 8;
    row = mask->data;
    for (y = 0, rows = mask->height; rows--; row += mask->stride, y++) {
    const uint8_t *byte_ptr = row;
    x = 0;
    py = _cairo_fixed_from_int (y);
    for (cols = bytes_per_row; cols--; ) {
        uint8_t byte = *byte_ptr++;
        if (byte == 0) {
        x += 8;
        continue;
        }

        byte = CAIRO_BITSWAP8_IF_LITTLE_ENDIAN (byte);
        for (bit = 1 << 7; bit && x < mask->width; bit >>= 1, x++) {
        if (byte & bit) {
            px = _cairo_fixed_from_int (x);
            status = _add_unit_rectangle_to_path (path,
                              px + x0,
                              py + y0);
            if (unlikely (status))
            goto BAIL;
        }
        }
    }
    }

BAIL:
    cairo_surface_destroy (&mask->base);

    return status;
}

cairo_status_t
_cairo_scaled_font_glyph_path (cairo_scaled_font_t *scaled_font,
                   const cairo_glyph_t *glyphs,
                   int          num_glyphs,
                   cairo_path_fixed_t  *path)
{
    cairo_status_t status;
    int i;

    status = scaled_font->status;
    if (unlikely (status))
    return status;

    _cairo_scaled_font_freeze_cache (scaled_font);
    for (i = 0; i < num_glyphs; i++) {
    cairo_scaled_glyph_t *scaled_glyph;

    status = _cairo_scaled_glyph_lookup (scaled_font,
                         glyphs[i].index,
                         CAIRO_SCALED_GLYPH_INFO_PATH,
                         &scaled_glyph);
    if (status == CAIRO_STATUS_SUCCESS) {
        status = _cairo_path_fixed_append (path,
                           scaled_glyph->path, CAIRO_DIRECTION_FORWARD,
                           _cairo_fixed_from_double (glyphs[i].x),
                           _cairo_fixed_from_double (glyphs[i].y));

    } else if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
        /* If the font is incapable of providing a path, then we'll
         * have to trace our own from a surface.
         */
        status = _cairo_scaled_glyph_lookup (scaled_font,
                         glyphs[i].index,
                         CAIRO_SCALED_GLYPH_INFO_SURFACE,
                         &scaled_glyph);
        if (unlikely (status))
        goto BAIL;

        status = _trace_mask_to_path (scaled_glyph->surface, path,
                      glyphs[i].x, glyphs[i].y);
    }

    if (unlikely (status))
        goto BAIL;
    }
  BAIL:
    _cairo_scaled_font_thaw_cache (scaled_font);

    return _cairo_scaled_font_set_error (scaled_font, status);
}

#endif